#pragma once

#include "util.hpp"
#include "db.hpp"
#include "room.hpp"
#include "matcher.hpp"
#include "online.hpp"
#include "room.hpp"
#include "session.hpp"

namespace liang
{
#define WWWROOT "./wwwroot/"

    class gobangServer
    {
    public:
        gobangServer(const std::string &host,
                     const std::string &user,
                     const std::string &password,
                     const std::string &dbname,
                     uint16_t port = 3306,
                     const std::string &wwwroot = WWWROOT)
            : _web_root(wwwroot), _ut(host, user, password, dbname, port), _rm(&_ut, &_om), _sm(&_wssrv), _mm(&_rm, &_om, &_ut)
        {
            _wssrv.set_access_channels(websocketpp::log::alevel::none);
            // 初始化asio调度器
            _wssrv.init_asio();
            _wssrv.set_reuse_addr(true);
            // 设置回调函数
            _wssrv.set_http_handler(std::bind(&gobangServer::http_callback, this, std::placeholders::_1));
            _wssrv.set_open_handler(std::bind(&gobangServer::wsopen_callback, this, std::placeholders::_1));
            _wssrv.set_close_handler(std::bind(&gobangServer::wsclose_callback, this, std::placeholders::_1));
            _wssrv.set_message_handler(std::bind(&gobangServer::wsmsg_callback, this, std::placeholders::_1, std::placeholders::_2));
        }
        ~gobangServer() {}

        void start(int port)
        {
            // 设置监听端口
            _wssrv.listen(port);
            // 开始获取新连接
            _wssrv.start_accept();
            // 启动服务器
            _wssrv.run();
        }

    private:
        // 静态资源请求
        void file_handler(websocket_server::connection_ptr &conn)
        {
            // 获取资源路径
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            std::string realpath = _web_root + uri;
            // 如果是一个目录
            if (realpath.back() == '/')
            {
                realpath += "login.html";
            }

            Json::Value resp;
            std::string body;
            bool ret = FileHelper::read(realpath, body);
            if (!ret)
            {
                FileHelper::read("./wwwroot/404.html", body);
                conn->set_status(websocketpp::http::status_code::not_found);
                conn->set_body(body);
            }
            else
            {
                conn->set_status(websocketpp::http::status_code::ok);
                conn->set_body(body);
            }
        }

        void httpResp(bool result, const std::string &reason, websocketpp::http::status_code::value code, websocket_server::connection_ptr &conn)
        {
            Json::Value resp;
            resp["result"] = result;
            resp["reason"] = reason;
            std::string resp_body;
            JsonHelper::serialize(resp, resp_body);
            conn->set_status(code);
            conn->set_body(resp_body);
            conn->append_header("Content-Type", "application/json");
        }

        // 注册功能
        void reg(websocket_server::connection_ptr &conn)
        {
            // 获取到注册的信息
            std::string req_body = conn->get_request_body();
            Json::Value reg_info;
            // 反序列化
            bool ret = JsonHelper::unserialize(req_body, reg_info);
            if (!ret)
            {
                DEBUG("注册请求反序列化失败");
                return httpResp(false, "请求的正文格式错误", websocketpp::http::status_code::bad_request, conn);
            }

            if (reg_info["username"].isNull() || reg_info["password"].isNull() ||
                reg_info["username"].asString().empty() ||
                reg_info["password"].asString().empty())
            {
                DEBUG("用户名或密码不完整");
                return httpResp(false, "请输入用户名和密码", websocketpp::http::status_code::bad_request, conn);
            }

            ret = _ut.registration(reg_info);
            if (!ret)
            {
                DEBUG("用户名被占用:%s", reg_info["username"].asCString());
                return httpResp(false, "用户名已经被占用", websocketpp::http::status_code::bad_request, conn);
            }

            DEBUG("用户注册成功:%s", reg_info["username"].asCString());
            httpResp(true, "注册成功", websocketpp::http::status_code::ok, conn);
        }

        // 登录功能
        void login(websocket_server::connection_ptr &conn)
        {
            // 获取请求正文
            std::string req_body = conn->get_request_body();
            // 反序列化
            Json::Value login_info;
            bool ret = JsonHelper::unserialize(req_body, login_info);
            if (!ret)
            {
                DEBUG("注册请求反序列化失败");
                return httpResp(false, "请求的正文格式错误", websocketpp::http::status_code::bad_request, conn);
            }
            // 查看请求是否完整
            if (login_info["username"].isNull() || login_info["password"].isNull() ||
                login_info["username"].asString().empty() ||
                login_info["password"].asString().empty())
            {
                DEBUG("用户名或密码不完整");
                return httpResp(false, "请输入用户名和密码", websocketpp::http::status_code::bad_request, conn);
            }
            // 校验数据库信息
            ret = _ut.login(login_info);
            if (!ret)
            {
                DEBUG("用户名密码错误");
                return httpResp(false, "用户名密码错误", websocketpp::http::status_code::bad_request, conn);
            }
            // 创建会话
            uint64_t uid = login_info["id"].asUInt64();
            session::ptr sp = _sm.createSession(uid, LOGIN);
            if (sp == nullptr)
            {
                DEBUG("创建session[%lu]失败", uid);
                return httpResp(false, "用户名密码错误", websocketpp::http::status_code::internal_server_error, conn);
            }
            _sm.setSessionExpireTime(sp->sid(), SESSION_TIMEOUT);
            // 设置响应头部
            std::string cookie_sid = "SID=" + std::to_string(sp->sid());
            conn->append_header("Set-Cookie", cookie_sid);
            DEBUG("用户[%lu]登录成功", uid);
            httpResp(true, "登录成功", websocketpp::http::status_code::ok, conn);
        }

        bool getValFromCookie(const std::string &cookie, const std::string &key, std::string &val)
        {
            std::vector<std::string> cookie_arry;
            StrHelper::split(cookie, "; ", cookie_arry);
            for (auto &str : cookie_arry)
            {
                std::vector<std::string> tarry;
                StrHelper::split(str, "=", tarry);
                if (tarry.size() != 2)
                    continue;
                if (tarry[0] == key)
                {
                    val = tarry[1];
                    return true;
                }
            }
            return false;
        }

        // 获取用户信息
        void info(websocket_server::connection_ptr &conn)
        {
            // 获取请求信息中的Cookie
            std::string cookie_str = conn->get_request_header("cookie");
            if (cookie_str.empty())
            {
                DEBUG("找不到cookie信息, 请重新登录");
                return httpResp(false, "找不到cookie信息, 请重新登录", websocketpp::http::status_code::bad_request, conn);
            }
            // 获取sid
            std::string sid_str;
            int ret = getValFromCookie(cookie_str, "SID", sid_str);
            if (!ret)
            {
                DEBUG("没有建立session");
                return httpResp(false, "没有建立session", websocketpp::http::status_code::bad_request, conn);
            }
            session::ptr sp = _sm.getSessionBySid(std::stol(sid_str));
            if (sp == nullptr)
            {
                DEBUG("登录过期, 请重新登录");
                return httpResp(false, "登录过期, 请重新登录", websocketpp::http::status_code::bad_request, conn);
            }
            // 从数据库中获取信息
            uint64_t uid = sp->getUser();
            Json::Value user;
            ret = _ut.selectById(uid, user);
            if (!ret)
            {
                DEBUG("没有找到用户[%lu]信息", uid);
                return httpResp(false, "没有找到用户信息", websocketpp::http::status_code::bad_request, conn);
            }
            // 序列化发送
            std::string body;
            JsonHelper::serialize(user, body);
            conn->set_body(body);
            conn->append_header("Content-Type", "application/json");
            conn->set_status(websocketpp::http::status_code::ok);
            // 刷新session过期时间
            _sm.setSessionExpireTime(std::stol(sid_str), SESSION_TIMEOUT);
        }

        void http_callback(websocketpp::connection_hdl hdl)
        {
            websocket_server::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string methon = req.get_method();
            std::string uri = req.get_uri();
            if (methon == "POST" && uri == "/reg")
            {
                reg(conn);
            }
            else if (methon == "POST" && uri == "/login")
            {
                login(conn);
            }
            else if (methon == "GET" && uri == "/info")
            {
                info(conn);
            }
            else
            {
                file_handler(conn);
            }
        }

        void wsResp(const std::string &optype, bool result, const std::string &reason, websocket_server::connection_ptr &conn)
        {
            Json::Value resp;
            resp["optype"] = optype;
            resp["result"] = result;
            resp["reason"] = reason;
            std::string body;
            JsonHelper::serialize(resp, body);
            conn->send(body);
        }

        session::ptr getSessionByCookie(websocket_server::connection_ptr &conn)
        {
            // 登录验证
            // 获取请求信息中的Cookie
            std::string cookie_str = conn->get_request_header("cookie");
            if (cookie_str.empty())
            {
                DEBUG("找不到cookie信息, 请重新登录");
                wsResp("", false, "找不到cookie信息, 请重新登录", conn);
                return nullptr;
            }
            // 获取sid
            std::string sid_str;
            int ret = getValFromCookie(cookie_str, "SID", sid_str);
            if (!ret)
            {
                DEBUG("没有建立session");
                wsResp("", false, "没有建立session", conn);
                return nullptr;
            }
            session::ptr sp = _sm.getSessionBySid(std::stol(sid_str));
            if (sp == nullptr)
            {
                DEBUG("登录过期, 请重新登录");
                wsResp("", false, "登录过期, 请重新登录", conn);
                return nullptr;
            }
            return sp;
        }

        void wsopenGameHall(websocket_server::connection_ptr &conn)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 判断是否重复登录
            if (_om.isInHall(sp->getUser()) || _om.isInRoom(sp->getUser()))
            {
                DEBUG("玩家[%lu]重复登录", sp->getUser());
                return wsResp("", false, "重复登录", conn);
            }
            // 加入游戏大厅
            _om.enterHall(sp->getUser(), conn);
            // 发送响应
            DEBUG("玩家[%lu]进入游戏大厅", sp->getUser());
            wsResp("hall_ready", true, "进入游戏大厅", conn);
            // 将session设置为永久
            _sm.setSessionExpireTime(sp->sid(), SESSION_FOREVER);
        }

        void wsopenGameRoom(websocket_server::connection_ptr &conn)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 判断是否重复登录
            if (_om.isInHall(sp->getUser()) || _om.isInRoom(sp->getUser()))
            {
                DEBUG("玩家[%lu]重复登录", sp->getUser());
                return wsResp("", false, "重复登录", conn);
            }
            // 判断房间是否存在
            room::ptr rp = _rm.getRoomByUid(sp->getUser());
            if (rp == nullptr)
            {
                DEBUG("玩家[%lu]房间不存在", sp->getUser());
                return wsResp("", false, "房间不存在", conn);
            }
            // 将用户添加到房间中
            _om.enterRoom(sp->getUser(), conn);
            // 将session设置为永久
            _sm.setSessionExpireTime(sp->sid(), SESSION_FOREVER);
            DEBUG("玩家[%lu]进入游戏房间[%lu]", sp->getUser(), rp->id());

            Json::Value resp;
            resp["optype"] = "room_ready";
            resp["result"] = true;
            resp["reason"] = "进入游戏房间";
            resp["room_id"] = rp->id();
            resp["uid"] = sp->getUser();
            resp["white_id"] = rp->getWhiteUser();
            resp["black_id"] = rp->getBlackUser();
            std::string body;
            JsonHelper::serialize(resp, body);
            conn->send(body);
        }

        void wsopen_callback(websocketpp::connection_hdl hdl)
        {
            websocket_server::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            if (uri == "/hall")
            {
                // 建立游戏大厅的长连接
                wsopenGameHall(conn);
            }
            else if (uri == "/room")
            {
                // 建立游戏房间的长连接
                wsopenGameRoom(conn);
            }
        }

        void wscloseGameHall(websocket_server::connection_ptr &conn)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 退出游戏大厅
            DEBUG("玩家[%lu]退出游戏大厅", sp->getUser());
            _om.exitHall(sp->getUser());
            // 设置session为临时
            _sm.setSessionExpireTime(sp->sid(), SESSION_TIMEOUT);
        }

        void wscloseGameRoom(websocket_server::connection_ptr &conn)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 退出游戏房间
            _om.exitRoom(sp->getUser());
            // 设置session为临时
            _sm.setSessionExpireTime(sp->sid(), SESSION_TIMEOUT);
            _rm.removeRoomUser(sp->getUser());
        }

        void wsclose_callback(websocketpp::connection_hdl hdl)
        {
            websocket_server::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            if (uri == "/hall")
            {
                // 关闭游戏大厅的长连接
                wscloseGameHall(conn);
            }
            else if (uri == "/room")
            {
                // 关闭游戏房间的长连接
                wscloseGameRoom(conn);
            }
        }

        void wsmsgGameHall(websocket_server::connection_ptr &conn, websocket_server::message_ptr &msg)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 获取请求信息
            std::string req_body = msg->get_payload();
            Json::Value req;
            bool ret = JsonHelper::unserialize(req_body, req);
            if (!ret)
            {
                DEBUG("请求信息解析失败");
                return wsResp("", false, "请求信息解析失败", conn);
            }
            if (!req["optype"].isNull() && req["optype"].asString() == "match_start")
            {
                _mm.add(sp->getUser());
                DEBUG("玩家[%lu]进入匹配队列", sp->getUser());
                return wsResp("match_start", true, "匹配开始", conn);
            }
            else if (!req["optype"].isNull() && req["optype"].asString() == "match_stop")
            {
                _mm.del(sp->getUser());
                DEBUG("玩家[%lu]取消匹配", sp->getUser());
                return wsResp("match_stop", true, "取消匹配", conn);
            }
            DEBUG("未知请求信息");
            wsResp("", false, "错误信息", conn);
        }

        void wsmsgGameRoom(websocket_server::connection_ptr &conn, websocket_server::message_ptr &msg)
        {
            session::ptr sp = getSessionByCookie(conn);
            if (sp == nullptr)
                return;
            // 获取房间信息
            room::ptr rp = _rm.getRoomByUid(sp->getUser());
            if (rp == nullptr)
            {
                DEBUG("玩家[%lu]房间不存在", sp->getUser());
                return wsResp("", false, "房间不存在", conn);
            }
            // 获取请求信息
            std::string req_body = msg->get_payload();
            Json::Value req;
            bool ret = JsonHelper::unserialize(req_body, req);
            if (!ret)
            {
                DEBUG("请求信息解析失败");
                return wsResp("", false, "请求信息解析失败", conn);
            }
            rp->handleRequest(req);
        }

        void wsmsg_callback(websocketpp::connection_hdl hdl, websocket_server::message_ptr msg)
        {
            websocket_server::connection_ptr conn = _wssrv.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            if (uri == "/hall")
            {
                // 游戏大厅的消息
                wsmsgGameHall(conn, msg);
            }
            else if (uri == "/room")
            {
                // 游戏房间的消息
                wsmsgGameRoom(conn, msg);
            }
        }

    private:
        websocket_server _wssrv;
        std::string _web_root;
        userTable _ut;
        onlineManager _om;
        roomManager _rm;
        sessionManager _sm;
        matcher _mm;
    };
}
